home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
Telnet 2.6.1d1 4⁄26⁄94 Folder
/
source
/
test
/
Audit.c
next >
Wrap
C/C++ Source or Header
|
1994-04-16
|
34KB
|
1,100 lines
#include "debug.h"
#ifdef SUPPORT_AUDIT
/* Audit.c */
#define PARANOIA 0
#include <Packages.h>
/*
* Audit.c
* Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
* Programmed by Martin Minow,
* Internet: minow@apple.com
* AppleLink: MINOW
* Version of January 14, 1993
*
* Edit History
* 93.01.09 MM First public release
* 93.01.14 MM Think and MPW generate different record sizes; a disaster if
* you create an Audit Record under Think and call Audit compiled
* under MPW. Also added a test for record sizes and included
* record size information in the AuditRecord.
* 93.01.21 MM Fix bug in procedure name recognizer that failed for certain
* pascal functions.
* 93.06.10 MM Cosmetic edits for Think C 6.0. No functional changes. Text
* was reformatted to fit into an "80-column" line.
* 93.08.14 MM Added "logicalRAMSize to the AuditRecord. This protects
* Audit (somewhat) against bad data forcing address errors.
*
* This library implements a drop-in logging capability for device drivers,
* callback functions, applications, and all other Macintosh code segments.
*
* The overall philosophy is that an application or driver allocates a record in
* the system heap that contains a small preamble and one or more 48 (decimal)
* byte blocks of data. The driver logs data to the log area, where each log
* record contains an identification value and up to four longwords of data.
*
* The user would then write a log control application that can turn logging on or
* off and dump/format the data in the log. The user can also use a MacsBug dcmd
* to display the current state of the log area.
*
* To add logging to a driver, add the following variable to the driver control
* record:
* AuditPtr auditPtr;
* and initialize the driver log (in the driver open routine) by calling
* devCtlEnt->auditPtr = InitAudit(
* gestaltSelector,
* nEntries,
* initiallyEnabled,
* preserveFirstFlag
* );
*
* The intialization routine creates an audit record in the System Heap and
* defines a gestalt selector with a pointer to the audit area. Once created,
* the record remains in the heap until the Macintosh is rebooted. The
* gestaltSelector should be unique to the driver. InitAudit need only be
* called once by any code segment that can allocate memory (i.e., by an
* application, INIT, or driver open function). Note that you do not
* need to check errors: if InitAudit fails to create (or locate) the record,
* it returns NULL and all other routines, upon receiving a NULL AuditPtr
* argument, do nothing gracefully.
*
* The log record contains a fixed header with the following information:
*
* version.u.low The earliest version of the AuditRecord that this instance
* of the library understands.
* version.u.high The latest version of the AuditRecord that this instance
* of the library understands.
* size.auditRecord The size of the Audit Record (excluding the dummy first
* entry). This protects against compiler alignment errors
* (which may occur if an Audit Record is created by code
* generated by one compiler, and accessed by code generated
* by a different compiler.
* size.auditEntry The size of the AuditQueueEntry. (See above.)
* free.Queue A standard O.S. queue with available data logging records.
* data.queue A standard O.S. queue with records waiting to be displayed
* or written to a file.
* lostData This counter is incremented when Audit function cannot
* obtain a record from the free queue.
* PSN Awaken this process when something is stored.
* refNum a void * (longword) that may be used by the calling
* software.
* isLogging TRUE if logging is enabled.
* timeAtStart GetDateTime() when the log was created.
* ticksAtStart TickCount() when the log was created.
* entries[] A vector of AuditEntry records.
*
* The audit user accesses the log record by calling functions: it should not
* access the structure directly.
*
* Each log record is stored in a AuditEntry structure that contains the
* following information:
* tickCount The Ticks value at the time the data was collected.
* lostData This is the number of records that were "lost" before this
* record because there was no free entry in the queue.
* idCode This longword identifies the log entry (i.e. who logged it).
* format This longword describes the format of the log data.
* data This 32 byte data area contains the log. If format is zero,
* the data is a Str31. Otherwise, it contains up to 8
* (sizeof data / sizeof (long)) longwords of data.
*
* Your application or driver calls the following functions to manage the log:
*
* AuditPtr InitAudit(
* OSType gestaltSelector,
* short nEntries
* Boolean initiallyEnabled,
* Boolean preserveFirst
* );
*
* Create the log record and "publish" a Gestalt selector with a pointer to the
* record. This is normally called by a driver when it is first opened.
* gestaltSelector is unique to the driver. InitAudit returns a pointer to the
* log record or NULL if it could not create the log. If the gestaltSelector was
* already registered, it returns a pointer to the already-created log without
* creating a new log.
*
* void Audit(
* AuditPtr auditPtr,
* OSType idCode,
* unsigned long format,
* ...
* );
*
* Store data in the log. idCode identifies this log request: by convention, it
* is an OSType. The format parameter simplifies display by defining the format
* of the subsequent data, The remaining parameters are log dependent. If auditPtr
* is NULL or logging disabled, the function does nothing, but does not fail.
*
* The AuditFormat macro is used to specify the format of the stored data:
*
* AuditFormat(arg1, arg2, ..., arg8)
*
* where arg1, etc. is specified by one of the following constants:
* kAuditFormatEnd No more data
* kAuditFormatSigned The datum is a 32-bit signed decimal integer
* kAuditFormatUnsigned The datum is a 32-bit unsigned decimal integer
* kAuditFormatHex The datum is a 32-bit address or 4-byte character
* kAuditFormatAddress The datum is a 32-bit hex value (never characters)
* kAuditFormatString The datum is a Pascal string. This must be the last
* (or only) argument. The string will be truncated
* to fit the remaining space. I.e. if it is the only
* argument, a 31-byte (plus one byte count) string
* will be stored.
* kAuditFormatLocation This must be the last format, but there is no
* associated value. Instead, the library stores
* the calling function name (the MacsBug name).
* The name will be truncated as for kAuditFormatString.
*
* Note that AuditFormat1, AuditFormat2, etc. macros are provided to simplify
* access to AuditFormat().
*
* Important: all parameters to Audit must be longwords. Short values (such as
* Booleans and OSErr codes) must be cast to long or unsigned long:
* Audit(auditPtr, 'Err!', AuditFormat1(kAuditFormatSigned),
* (long) statusError);
* If you do not do this, the data may be logged incorrectly or, in extreme
* cases, your program may crash.
*
***
* Boolean AuditRead(
* AuditPtr auditPtr,
* AuditEntry *thisAuditEntry
* );
* An application program calls AuditRead periodically to retrieve data from the
* log. If there is a log entry to process, it is copied to thisAuditEntry and
* the function returns TRUE. If it returns FALSE, there is no data waiting. This
* function should be called from the log application's event loop. AuditRead
* manages all log queues. Note that reading the log resets the missedDataCount.
*
* Each log record is time-stamped by the following algorithm:
* GetAuditStartTimes(auditPtr, &gTimeAtStart, &gTicksAtStart);
* if (AuditRead(auditPtr, &thisAuditEntry)) {
* elapsedTicks = thisAuditEntry.tickCount - gTicksAtStart;
* SecsToDate(
* gTimeAtStart + (elapsedTicks / 60),
* &logEntryDateString
* );
* printf(, ..., date.second, elapsedTicks % 60);
* }
* See AuditEntryFormat.c for details.
*
***
* AuditPtr GetAuditPtr(
* OSType gestaltSelector
* );
*
* Return a pointer to the common storage area, if it exists. This returns NULL
* if there is no log "registered" by that name.
*
***
* void WakeUpAudit(
* AuditPtr auditPtr,
* ProcessSerialNumber *oldPSN
* );
*
* Wake up this process (application) when something is logged: called by:
* GetCurrentProcess((&oldPSN);
* WakeUpAudit(auditPtr, &oldPSN);
* ... logging stuff ...
* WakeUpAudit(auditPtr, &oldPSN); // restore previous
* ExitToShell();
*
***
* Boolean EnableAudit(
* AuditPtr auditPtr,
* Boolean enableLogging
* );
*
* Enable or disable logging. This returns the previous logging value. At
* initialization, a compile-time parameter sets the initial logging value.
*
***
* Boolean IsAuditEnabled(
* AuditPtr auditPtr
* );
*
* Return TRUE if auditing is enabled for this audit record.
*
***
* void GetAuditStartTimes(
* AuditPtr auditPtr,
* unsigned long *timeAtStart,
* unsigned long *ticksAtStart
* );
* Return the times that the log was created. Using these values, the log display
* application can time-stamp all log entries.
*
***
* void SetAuditRefNum(
* AuditPtr auditPtr,
* void *refNum
* );
* Store a user-controlled reference value. This may be coerced to any scalar
* value (such as a memory pointer or longword).
*
***
* void *GetAuditRefNum(
* AuditPtr auditPtr
* );
* Return the current user-controlled reference value. This may be coerced to any
* scalar value (such as a memory pointer or longword). This returns zero if
* auditPtr is NULL or no value had been stored.
*/
#include "Audit.h"
#include <Types.h>
#include <Traps.h>
#include <Errors.h>
#include <Memory.h>
#include <GestaltEqu.h>
#include <OSUtils.h>
#ifndef PARANOIA
#define PARANOIA 0
#endif
/*
* The version id's are defined as (majorVersion << 8) | minorVersion
*/
#define kAuditEarliestVersion ((1 << 8) | 2)
#define kAuditLatestVersion ((1 << 8) | 2)
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
/*
* This is Think C specific, and possibly release dependent.
*/
#if defined(_Gestalt) == FALSE && defined(_GestaltDispatch)
#define _Gestalt _GestaltDispatch
#endif
/*
* Note that this file contains 68000 assembly. This must be modified when
* the Audit Library is converted to Power PC.
*/
/*
* These values must match values in AuditDCMD.c
*/
#define kAuditMagicHeaderSize 7 /* Size in longwords */
#define kAuditMagicHeader_0 0x4E560000 /* First word in Gestalt result */
#if 0
/*
* The following sequence was used to create the GestaltSelector stub. It is
* never compiled directly, but was pasted into a temporary file which was
* decompiled to get the machine code.
*/
static pascal OSErr GestaltSelector(
OSType selector,
long *result
)
{
asm {
/*
* 22 was determined by painful observation and many pleasant
* encounters with MacsBug.
*/
lea 22(pc),a0 /* a0 = Address of auditRecord */
movea.l result,a1 /* a1 -> result */
move.l a0,(a1) /* Store result */
}
return (noErr);
}
#endif
/*
* DisableInterrupts and EnableInterrupts manipulate the 68000 status register.
* They are expanded inline. A comment consisting of *** in the left margin
* indicates code that is run with interrupts disabled.
*
* unsigned short DisableInterrupts(void) {
* asm {
* move sr,d0
* move #0x2600,sr
* }
*/
unsigned short DisableInterrupts(void) = { 0x40C0, 0x46FC, 0x2600 };
#ifdef THINK_C
/*
* Since Think C has a real inline assembler, we can save one or two instructions.
* Nerd fun.
*/
#define EnableInterrupts(saveSR) asm { \
move saveSR,sr \
}
#else
/*
* Even though the saveSR argument is a short, we must declare the function as a
* long so that both Think and MPW generate the same code. Otherwise, Think pushes
* a short and 2(sp) addresses the wrong value. (Guess how I discovered this.)
* Using the Think C inline defined above also eliminates the problem.
*
* void EnableInterrupts(unsigned long saveSR) {
* asm {
* move 2(sp),sr
* }
*/
void EnableInterrupts(unsigned long saveSR) = { 0x46EF, 0x0002 };
#endif
/*
* This inline copies the value of A6 (the frame pointer) to D0. It is needed in
* order to get the caller's function name.
*/
unsigned long *GetA6(void) = { 0x200E }; /* move.l A6,D0 */
#define LOG (*auditPtr)
#define ENTRY (entryPtr->theEntry)
static void StoreString(
const StringPtr theString, /* Source, length may be wrong */
unsigned short stringLength, /* Purported actual length */
unsigned short maxLength, /* Maximum destination length */
StringPtr result /* Destination pointer */
);
static AuditQueueEntryPtr GetQueueEntry(
QHdrPtr theQueue
);
#define PutQueueEntry(theQueue, theEntry) do { \
Enqueue((QElemPtr) &theEntry->qLink, theQueue); \
} while (0)
static Boolean TrapAvailable(
short theTrap
);
static void GetCallerName(
unsigned long *destPtr,
unsigned short maxString,
unsigned long *a6
);
/*
* InitAudit
*/
AuditPtr
InitAudit(
OSType gestaltSelector,
unsigned short nEntries,
Boolean initiallyEnabled,
Boolean preserveFirst
)
{
register unsigned long *recordPtr;
register AuditPtr auditPtr;
register AuditQueueEntryPtr queueEntryPtr;
Size logRecordSize;
short i;
OSErr status;
/*
* This will not compile on either Think or MPW if the sizes are correct.
* Unfortunately, sizeof cannot be incorporated into a #define, so we
* have to do this at execution time.
*/
if (sizeof (AuditEntry) != kSizeofAuditEntry)
DebugStr("\pAuditEntry size error");
if (sizeof (AuditQueueEntry) != kSizeofAuditQueueEntry)
DebugStr("\pAuditQueueEntry size error");
if (sizeof (AuditRecord) != kSizeofAuditRecord)
DebugStr("\pAuditRecord size error");
/*
* Presuppose problems and exit if the system is so old that it doesn't
* even support Gestalt. This is slightly overkill, as the current
* Gestalt glue does the TrapAvailable check for us.
*/
auditPtr = NULL;
if (TrapAvailable(_Gestalt) == FALSE)
goto exit;
logRecordSize = sizeof (AuditRecord)
+ (kAuditMagicHeaderSize * sizeof (long))
+ ((nEntries - 1) * sizeof (AuditQueueEntry));
#if PARANOIA > 1
{
Str255 foo;
NumToString(sizeof (AuditQueueEntry), foo);
DebugStr(foo);
NumToString(logRecordSize, foo);
DebugStr(foo);
}
#endif
recordPtr = (unsigned long *) NewPtrSysClear(logRecordSize);
if (recordPtr == NULL)
goto exit;
auditPtr = (AuditPtr) &recordPtr[kAuditMagicHeaderSize];
/*
* The code for the Gestalt Selector function precedes the actual audit
* record. Gestalt calls this function when a program calls GetAuditPtr
* for our selector. The function returns the address of the AuditRecord
* as the selector value. This code will need revision when run on
* non-Motorola cpu's. See the definition of GestaltSelector above for
* the original code.
*
* asm {
* link a6,#0000 Create stack frame 0x4E56 0x0000
* lea 22(pc),a0 a0 -> auditRecord 0x41FA 0x0016
* movea.l 8(a6),a1 a1 -> response 0x226E 0x0008
* move.l a0,(a1) Store result 0x2288
* clr.w 8(a6) Result := noErr 0x426E 0x0010
* unlk a6 Delete stack frame 0x4E5E
* movea.l (a7)+,a0 Pop return address 0x205F
* addq.l #0x8,a7 Clear stack params 0x508F
* jmp (a0) Return to caller 0x4ED0
* dc.w 0 Fake MacsBug name 0x0000
* ** AuditRecord starts here **
* };
*/
#if kAuditMagicHeader_0 != 0x4E560000
<< error, the following won't work: see AuditDCMD.c >>
#endif
recordPtr[0] = 0x4E560000;
recordPtr[1] = 0x41FA0016;
recordPtr[2] = 0x226E0008;
recordPtr[3] = 0x2288426E;
recordPtr[4] = 0x00104E5E;
recordPtr[5] = 0x205F508F;
recordPtr[6] = 0x4ED00000; /* Pad with fake MacsBug name */
#if kAuditMagicHeaderSize != 7
<< error, the above won't work: see AuditDCMD.c >>
#endif
/*
* If this machine has a data/instruction cache, flush it so that we
* cannot execute stale data.
*/
if (TrapAvailable(_HWPriv)) {
FlushDataCache();
FlushInstructionCache();
}
/*
* Add this as a gestalt. Errors present problems: either this was already
* added (no big deal), or the luser is trying to redefine a "normal"
* Gestalt. We check by trying to get the AuditPtr and hope that
* GetAuditPtr's internal checks ferret out an attempt to redefine a
* normal Gestalt.
*/
status = NewGestalt(gestaltSelector, (ProcPtr) recordPtr);
if (status == gestaltDupSelectorErr) {
/*
* Trouble! this has already been added! Your program
* may eventually crash if the gestalt is not an audit
* record: see GetAuditPtr() for the safety checks.
*/
DisposPtr((Ptr) auditPtr);
auditPtr = GetAuditPtr(gestaltSelector);
goto exit;
}
else if (status != noErr) {
/*
* Something is seriously out of order. Just return NULL.
*/
DisposPtr((Ptr) recordPtr);
auditPtr = NULL;
goto exit;
}
/*
* Setup the rest of the AuditRecord.
*/
LOG.version.u.low = kAuditEarliestVersion;
LOG.version.u.high = kAuditLatestVersion;
LOG.recordSize = kAuditRecordSize;
/*
* Store the top memory location in the AuditRecord. This helps protect
* us from crashes in Audit if the caller passes a garbage StringPtr.
*/
Gestalt(gestaltLogicalRAMSize, (long *) &LOG.logicalRAMSize);
GetDateTime(&LOG.timeAtStart);
LOG.ticksAtStart = TickCount();
LOG.PSN.lowLongOfPSN = kNoProcess;
/*
* Ok, so far. Now build the initial free queue.
*/
queueEntryPtr = &LOG.entries[0];
for (i = 0; i < nEntries; i++) {
PutQueueEntry(&LOG.free.queue, queueEntryPtr);
++queueEntryPtr;
}
exit: EnableAudit(auditPtr, initiallyEnabled);
PreserveAudit(auditPtr, preserveFirst);
return (auditPtr);
}
/*
* This function logs data if logging is enabled.
*
* auditPtr As returned by InitAudit. If NULL, nothing is logged.
* idCode A user-controlled value, by convention an OSType (4-byte
* character), that identifies this entry. The display program
* prints it.
* format The format of the remaining data: kAuditFormatString if the
* datum is a pascal string, otherwise, it is as created by the
* AuditFormat macro.
* ... Additional data as needed. These values must be cast to
* longwords to prevent ThinkC/MPW incompatibilities.
*/
void
Audit(
register AuditPtr auditPtr,
OSType idCode,
unsigned long format,
...
)
{
unsigned short saveSR;
va_list argPtr;
register unsigned long *destPtr;
unsigned long tickCount;
unsigned short maxString;
unsigned short thisFormat;
StringPtr theString;
unsigned char hexString[10];
register AuditQueueEntryPtr entryPtr;
register AuditQueueEntryPtr nextPtr;
tickCount = TickCount();
if (auditPtr != NULL && (LOG.flags & kAuditEnabledMask) != 0) {
entryPtr = GetQueueEntry(&LOG.free.queue);
/*
* Note: *** in the left-most column indicates code that is executed
* with interrupts disabled.
*/
saveSR = DisableInterrupts();
/***/ if (entryPtr == NULL) {
/***/ ++LOG.lostData;
/***/ EnableInterrupts(saveSR);
if ((LOG.flags & kAuditPreserveFirstMask) == 0) {
/*
* We really want to log the latest entry. Remove the first
* "to be displayed" entry. If successful, it will get the new
* record and the next "to be displayed" entry, if any, gets
* the lost data counter.
*/
entryPtr = GetQueueEntry(&LOG.data.queue);
if (entryPtr == NULL)
; /* goto return */
else {
/***/ saveSR = DisableInterrupts();
/***/ LOG.lostData += ENTRY.lostData;
/***/ nextPtr = (AuditQueueEntryPtr) LOG.data.queue.qHead;
/***/ if (nextPtr != NULL) {
/***/ nextPtr->theEntry.lostData += LOG.lostData;
/***/ LOG.lostData = 0;
/***/ }
/***/ goto returnThisEntry;
}
}
}
/***/ else {
returnThisEntry:
/***/ ENTRY.lostData = LOG.lostData;
/***/ LOG.lostData = 0;
/***/ EnableInterrupts(saveSR);
ENTRY.tickCount = tickCount;
ENTRY.idCode = idCode;
ENTRY.format = format;
va_start(argPtr, format);
destPtr = ENTRY.data;
maxString = sizeof (ENTRY.data);
while (maxString > 0) {
thisFormat = format & kAuditFormatMask;
switch (thisFormat) {
case kAuditFormatEnd:
goto exitLoop;
case kAuditFormatString:
theString = va_arg(argPtr, StringPtr);
/*
* Make sure this is a real string pointer. Otherwise, if
* it's bogus, theString[0] will cause an address error.
*/
theString = (StringPtr) StripAddress((Ptr) theString);
if (theString == NULL
|| (unsigned long) theString >= LOG.logicalRAMSize) {
short i;
unsigned short value;
/*
* Bogus string pointer (NULL or otherwise extreme):
* store the Hex address in the audit record.
*/
for (i = 10; i > 1; i--) {
value = ((unsigned long) theString) & 0x0F;
*((unsigned long *) &theString) >>= 4;
hexString[i] = (value < 10)
? value + '0' : value + ('A' - 10);
}
hexString[1] = '?';
hexString[0] = 9;
theString = hexString;
}
StoreString(
theString,
theString[0],
maxString,
(StringPtr) destPtr
);
goto exitLoop;
case kAuditFormatLocation:
GetCallerName(destPtr, maxString, GetA6());
goto exitLoop;
default:
*destPtr++ = va_arg(argPtr, unsigned long);
format >>= kAuditFormatShift;
maxString -= sizeof (unsigned long);
break;
}
}
exitLoop: va_end(nextArg);
PutQueueEntry(&LOG.data.queue, entryPtr);
if (LOG.PSN.highLongOfPSN != 0
|| LOG.PSN.lowLongOfPSN != kNoProcess)
(void) WakeUpProcess(&LOG.PSN);
}
}
#undef DATA
}
/*
* The following functions are normally called by the display application:
*/
/*
* Return a pointer to the log area, or NULL if there is none. Note that we make
* a few sanity checks to better ensure that the gestaltSelector actually refers
* to a Audit record.
*/
AuditPtr
GetAuditPtr(
OSType gestaltSelector
)
{
auto long gestaltResponse;
register AuditPtr auditPtr;
if (TrapAvailable(_Gestalt) == FALSE
|| Gestalt(gestaltSelector, &gestaltResponse) != noErr
|| gestaltResponse == 0
|| (gestaltResponse & 0x3) != 0)
auditPtr = NULL;
else {
auditPtr = (AuditPtr) gestaltResponse;
/*
* Sanity check: make sure the result is really an Audit Record and
* we can deal with the library release that created it.
*/
if (((unsigned long *) auditPtr)[-kAuditMagicHeaderSize]
!= kAuditMagicHeader_0
|| LOG.version.u.low < kAuditEarliestVersion
|| LOG.version.u.high > kAuditLatestVersion
|| LOG.recordSize != kAuditRecordSize)
auditPtr = NULL;
}
return (auditPtr);
}
/*
* Awaken a specified process. Call by the following sequence:
* GetCurrentProcess(&oldPSN); // process to awaken
* WakeUpAudit(auditPtr, &oldPSN);
* The previous process is now stored in oldPSN)
* ... display the log ...
* WakeUpAudit(auditPtr, &oldPSN); // restore old
* ExitToShell();
*/
void
WakeUpAudit(
register AuditPtr auditPtr,
ProcessSerialNumber *oldPSN
)
{
ProcessSerialNumber previousPSN;
short saveSR;
long gestaltResult;
OSErr status;
if (auditPtr != NULL) {
status = Gestalt(gestaltOSAttr, &gestaltResult);
if (status != noErr
|| (gestaltResult & (1 << gestaltLaunchControl)) == 0) {
oldPSN->highLongOfPSN = 0;
oldPSN->lowLongOfPSN = kNoProcess;
}
/***/ saveSR = DisableInterrupts();
/***/ previousPSN = LOG.PSN;
/***/ LOG.PSN = *oldPSN;
/***/ EnableInterrupts(saveSR);
*oldPSN = previousPSN;
}
}
/*
* Enable/disable logging. Returns the old logging state.
*/
Boolean
EnableAudit(
register AuditPtr auditPtr,
Boolean enableLogging
)
{
Boolean oldLogState;
short saveSR;
if (auditPtr == NULL)
oldLogState = FALSE;
else {
/***/ saveSR = DisableInterrupts();
/***/ oldLogState = (LOG.flags & kAuditEnabledMask) != 0;
/***/ LOG.flags &= ~kAuditEnabledMask;
/***/ if (enableLogging)
/***/ LOG.flags |= kAuditEnabledMask;
/***/ EnableInterrupts(saveSR);
}
return (oldLogState);
}
/*
* Return the value of the Audit enable flag. Returns FALSE if auditPtr is NULL
* or auditing is disabled.
*/
Boolean
IsAuditEnabled(
AuditPtr auditPtr
)
{
if (auditPtr == NULL)
return (FALSE);
else {
return ((LOG.flags & kAuditEnabledMask) != 0);
}
}
/*
* Enable/disable logging. Returns the old logging state.
*/
Boolean
PreserveAudit(
register AuditPtr auditPtr,
Boolean preserveFirst
)
{
Boolean oldPreserveState;
short saveSR;
if (auditPtr == NULL)
oldPreserveState = FALSE;
else {
/***/ saveSR = DisableInterrupts();
/***/ oldPreserveState = (LOG.flags & kAuditPreserveFirstMask) != 0;
/***/ LOG.flags &= ~kAuditPreserveFirstMask;
/***/ if (preserveFirst)
/***/ LOG.flags |= kAuditPreserveFirstMask;
/***/ EnableInterrupts(saveSR);
}
return (oldPreserveState);
}
/*
* Get the time that the log record was created. This is used to time-stamp log
* entries. Since the time-stamp is created only when the log is created (and
* before it is visible through Gestalt), we don't need to lock out interrupts.
*/
void
GetAuditStartTimes(
register AuditPtr auditPtr,
unsigned long *timeAtStart,
unsigned long *ticksAtStart
)
{
if (auditPtr == NULL) {
*timeAtStart = 0;
*ticksAtStart = 0;
}
else {
*timeAtStart = LOG.timeAtStart;
*ticksAtStart = LOG.ticksAtStart;
}
}
/*
* Read the next log entry. This returns a copy of the log entry, if one is
* available and returns TRUE. This function manages all log queues. If no entry
* is available, or logging is disabled, the function returns FALSE.
*/
Boolean
ReadAudit(
register AuditPtr auditPtr,
AuditEntryPtr thisAuditEntry
)
{
register AuditQueueEntryPtr entryPtr;
if (auditPtr == NULL)
return (FALSE);
else {
entryPtr = GetQueueEntry(&LOG.data.queue);
if (entryPtr == NULL)
return (FALSE);
else {
*thisAuditEntry = ENTRY;
PutQueueEntry(&LOG.free.queue, entryPtr);
return (TRUE);
}
}
}
/*
* Store a user-controlled reference value. This may be coerced to any scalar
* value (such as a memory pointer or longword). SetAuditRefNum returns the
* current value of the refNum, or zero if auditPtr is NULL.
*/
void
*SetAuditRefNum(
AuditPtr auditPtr,
void *refNum
)
{
void *result;
short saveSR;
if (auditPtr == NULL)
result = NULL;
else {
/***/ saveSR = DisableInterrupts();
/***/ result = LOG.refNum;
/***/ LOG.refNum = refNum;
/***/ EnableInterrupts(saveSR);
}
return (result);
}
/*
* Return the current user-controlled reference value. This may be coerced to any
* scalar value (such as a memory pointer or longword). This returns zero if
* auditPtr is NULL or no value had been stored.
*/
void *
GetAuditRefNum(
AuditPtr auditPtr
)
{
/*
* Note that we assume that we can retrieve LOG.refNum without
* interference from interrupt routines. Is this reasonable?
*/
return ((auditPtr == NULL) ? NULL : LOG.refNum);
}
/*
* Remove the first entry from the queue, return NULL on failure.
*/
static AuditQueueEntryPtr
GetQueueEntry(
QHdrPtr theQueue
)
{
register QElemPtr qElemPtr;
if ((qElemPtr = theQueue->qHead) != NULL) {
if (Dequeue(qElemPtr, theQueue) != noErr)
qElemPtr = NULL;
}
return ((AuditQueueEntryPtr) qElemPtr);
}
/*
* TrapAvailable (see Inside Mac VI 3-8)
*/
#define NumToolboxTraps() ( \
(NGetTrapAddress(_InitGraf, ToolTrap) \
== NGetTrapAddress(0xAA6E, ToolTrap)) \
? 0x200 : 0x400 \
)
#define GetTrapType(theTrap) ( \
((theTrap) & 0x800 != 0) ? ToolTrap : OSTrap \
)
static Boolean
TrapAvailable(
short theTrap
)
{
TrapType trapType;
trapType = GetTrapType(theTrap);
if (trapType == ToolTrap) {
theTrap &= 0x07FF;
if (theTrap >= NumToolboxTraps())
theTrap = _Unimplemented;
}
return (
NGetTrapAddress(theTrap, trapType)
!= NGetTrapAddress(_Unimplemented, ToolTrap)
);
}
/*
* GetCallerName peeks up the calling chain list to the Audit function caller. It
* then scans through the function code to locate the start and end of the
* function. The end of the function is followed by the MacsBug name (maybe).
* This is based on ShowStackChain.c by Greg Anderson greggor@apple.com
*/
static void
GetCallerName(
unsigned long *destPtr,
unsigned short maxString,
unsigned long *a6
)
{
unsigned long returnAddress;
register unsigned short *pc;
short maxSearch;
unsigned short length;
Str31 tempString;
unsigned long pcOffset;
#define pcByte ((unsigned char *) pc)
returnAddress = a6[1]; /* Return address to Audit caller */
/*
* First, look forwards for the symbol name
*/
pc = (unsigned short *) returnAddress;
tempString[0] = 0;
for (maxSearch = 0; maxSearch < 0x4000; maxSearch++, pc++) {
if (pc[0] == 0x4E56) /* link */
break; /* We've gone too far */
if (pc[0] == 0x4E75 /* rts */
|| pc[0] == 0x4ED0 /* jmp (a0) */
|| pc[0] == 0x4E74) { /* rtd #N */
if (pc[0] == 0x4E74) /* If rtd, skip over displacement */
pc++;
pc++; /* Skip over return instruction */
if (pc[0] != 0x4E56) { /* Link instruction means no name */
/*
* pc now points to the word following the RTS. Use the
* MacsBug naming conventions to determine the function name,
* if any. See Appendix D in the MacsBug User's Guide.
*/
length = pcByte[0] & 0x7F;
if (length >= 0x20 && length <= 0x7F) {
/*
* Fixed length format: first byte is in the range 0x20
* through 0x7F, the high bit may or may not be set.
*/
if ((pcByte[1] & 0x80) != 0) {
/*
* Pascal 16-byte class name format. The string is
* stored "method" "class" (each takes 8 bytes) --
* MacsBug swaps the order and inserts a '.'
* (Warning, this is untested.)
*/
BlockMove(&pcByte[0], &tempString[10], 8);
BlockMove(&pcByte[7], &tempString[1], 8);
tempString[10] &= ~0x80;
tempString[11] &= ~0x80;
tempString[9] = '.';
tempString[0] = 17;
}
else {
/*
* Pascal 8 byte format.
*/
BlockMove(&pcByte[0], &tempString[1], 8);
tempString[0] = 7;
}
}
else if (pcByte[0] >= 0x80 && pcByte[0] <= 0x9F) {
/*
* Variable-length string format. If the length byte is
* zero after removing the flag bit, the next byte has
* the true (Str255) length.
*/
if (length == 0) {
/*
* Step over the flag. Note that pc and pcByte are the
* same variable (pc is a "short *" while pcByte is a
* "char *"). The next statement adds 1 to pc in a way
* that doesn't cause ANSI compilers to lose their
* electronic lunches.
*/
pc = (unsigned short *) &pcByte[1];
length = pcByte[0];
}
StoreString(
pcByte,
length,
sizeof (tempString) - 1,
tempString
);
}
else {
/*
* Something isn't understandable. Do nothing. This will
* be caught by the "if there is no symbol" test below.
*/
}
}
break;
}
}
/*
* If there is no symbol name, don't search for an offset.
*/
pcOffset = 0;
if (tempString[0] != 0) {
pc = (unsigned short *) returnAddress;
for (maxSearch = 0; maxSearch < 0x4000; maxSearch++) {
if (pc[0] == 0x4E56) { /* link instruction */
pcOffset =
((unsigned long) returnAddress)
- ((unsigned long) pc);
break;
}
--pc;
}
}
if (pcOffset == 0 || tempString[0] == 0) {
/*
* Since we don't have a MacsBug name, set the offset value to zero
* (this will act as a signal) and store the return address in the
* second data value.
*/
if (maxString >= sizeof (unsigned long)) {
*destPtr++ = 0;
maxString -= sizeof (unsigned long);
}
if (maxString >= sizeof (unsigned long)) {
*destPtr++ = returnAddress;
maxString -= sizeof (unsigned long);
}
}
else {
if (maxString >= sizeof (unsigned long)) {
*destPtr++ = pcOffset;
maxString -= sizeof (unsigned long);
StoreString(
tempString,
tempString[0],
maxString,
(StringPtr) destPtr
);
}
}
#undef pcByte
}
/*
* Copy the source string to the destination, respecting the maximum size.
*/
static void
StoreString(
const StringPtr theString,
unsigned short stringLength,
unsigned short maxLength,
StringPtr result
)
{
if (stringLength >= maxLength)
stringLength = maxLength - 1;
BlockMove(theString, result, stringLength + 1);
result[0] = stringLength;
}
#endif // SUPPORT_AUDIT